<template>
	<view>
		<view class="item-wrap" :style="{ height: itemWrapHeight + 'px' }">
			<view
				v-for="(item, index) in list"
				:key="item.id"
				class="item"
				:class="{
					cur: cur == item.id,
					zIndex: curZ == item.id,
					itemTransition: itemTransition
				}"
				:style="{
					width: 100 / columns + '%',
					transform: 'translate3d(' +
						(cur == item.id ? tranX : item.tranX) + 'px,' +
						(cur == item.id ? tranY : item.tranY) + 'px,0px)'
				}"
				@longpress="longPress($event, item)"
				@touchstart="touchStart($event, item)"
				@touchmove.prevent.stop="touchMove($event, item)"
				@touchend="touchEnd"
				@click="preview(item)"
			>
				<view class="info">
					<view class="info-box">
						<image
							class="info-box-image"
							:src="item.data.path | media('xc200')"
							mode="aspectFill"
						></image>
						<view class="load-wrap" v-if="item.data.status === 0">加载中</view>
						<view v-if="!disabled" class="remove" @click.stop="removeItem(item.key, index)">
							<text
								class="iconfont icon-close-circle-fill"
								:style="{
									fontSize: removeSize + 'rpx',
									lineHeight: removeSize + 'rpx'
								}"
							></text>
						</view>
					</view>
				</view>
			</view>
			<slot></slot>
		</view>
	</view>
</template>

<script>
export default {
	props: {
		columns: {
			type: Number,
			default: 3
		},
		listData: {
			type: Array,
			default() {
				return [];
			}
		},
		long: {
			type: Boolean,
			default: false
		},
		disabled: {
			type: Boolean,
			default: false
		},
		uploading: {
			type: Boolean,
			default: false
		},
		removeSize: {
			type: [Number, String],
			default: 40
		}
	},
	data() {
		return {
			list: [],
			windowHeight: 0,
			itemWrapHeight: 0,
			item: null,
			itemWrap: null,
			itemTransition: false,
			touch: false,
			startX: 0,
			startY: 0,
			oldX: 0,
			oldY: 0,
			tranX: 0,
			tranY: 0,
			cur: -1,
			curZ: -1,
			originKey: -1
		};
	},
	watch: {
		listData(newVal, oldVal) {
			let oldLen = this.list.length;
			let newLen = newVal.length;
			if (oldLen === 0) {
				// 初始化
				this.init();
			} else if (newLen === oldLen) {
				// 状态变化
				this.updateList({ tag: 'status', newLen, oldLen });
			} else if (newLen > oldLen) {
				// 新增item
				this.updateList({ tag: 'add', newLen, oldLen });
				this.changeWrapHeight();
				this.getPosition(this.list);
			} else {
				// 删除item
				// 删除时直接处理了list，所以不会出现这种情况
			}
		}
	},
	mounted() {
		if (this.listData.length !== 0) {
			this.init();
		}
	},
	methods: {
		init() {
			this.initList();
			if (!this.item) {
				this.initLayoutInfo();
			} else {
				this.getPosition(this.list, false);
				this.changeWrapHeight();
			}
		},
		initList() {
			this.list = this.listData.map((item, index) => {
				let data = {
					fixed: item.fixed,
					key: index,
					id: item.id,
					tranX: 0,
					tranY: 0,
					data: item
				};
				return data;
			});
		},
		initLayoutInfo() {
			this.windowHeight = uni.getSystemInfoSync().windowHeight;
			setTimeout(() => {
				this.createSelectorQuery()
					.select('.item')
					.boundingClientRect(res => {
						this.item = res;
						this.changeWrapHeight();
						this.getPosition(this.list, false);

						this.createSelectorQuery()
							.select('.item-wrap')
							.boundingClientRect(res => {
								this.itemWrap = res;
							})
							.exec();
					})
					.exec();
			});
		},
		changeWrapHeight() {
			let rows = Math.ceil(this.list.length / this.columns);
			this.itemWrapHeight = rows * this.item.height;
		},
		getPosition(data, isInsert = true) {
			this.list = data.map((item, index) => {
				item.tranX = this.item.width * (item.key % this.columns);
				item.tranY = Math.floor(item.key / this.columns) * this.item.height;
				return item;
			});
			if (!isInsert) return;

			let listData = [];
			this.itemTransition = true;
			this.list.forEach(item => {
				listData[item.key] = item.data;
			});
			this.$emit('change', listData);
		},
		updateList(options) {
			if (options.tag === 'add') {
				this.updateListAdd(options);
			} else {
				this.updateListStatus(options);
			}
		},
		updateListAdd(options) {
			for (let i = options.oldLen; i < options.newLen; i++) {
				let item = this.listData[i];
				let data = {
					fixed: item.fixed,
					key: i,
					id: item.id,
					tranX: this.item.width * (i % this.columns),
					tranY: Math.floor(i / this.columns) * this.item.height,
					data: item
				};
				this.list.push(data);
			}
		},
		updateListStatus(options) {
			for (let i = 0; i < options.newLen; i++) {
				if (this.list[i].data.status !== this.listData[i].status) {
					let item = this.list[i];
					item.data = this.listData[i];
					this.$set(this.list, i, item);
					break;
				}
			}
			// // #ifdef MP-WEIXIN
			// this.setData({
			// 	list: listData
			// });
			// // #endif
			// // #ifndef MP-WEIXIN
			// this.list = listData;
			// // #endif
		},
		touchStart(e, item) {
			if (this.long || this.disabled || this.uploading) return;
			this.touchInit(e, item);
		},
		longPress(e, item) {
			if (!this.long || this.disabled || this.uploading) return;
			this.touchInit(e, item);
		},
		touchInit(e, item) {
			let index = item.key;
			this.touch = true;
			this.startX = e.changedTouches[0].pageX;
			this.startY = e.changedTouches[0].pageY;

			if (this.columns === 1) {
				// 单列时候X轴初始不做位移
				this.oldX = 0;
			} else {
				// 多列的时候计算X轴初始位移, 使 item 保持原位置不变
				this.oldX = this.item.width * (index % this.columns);
			}

			// 计算Y轴初始位移, 使 item 保持原位置不变
			this.oldY = Math.floor(index / this.columns) * this.item.height;

			this.cur = item.id;
			this.curZ = item.id;
			this.tranX = this.oldX;
			this.tranY = this.oldY;
		},
		touchMove(e, item) {
			if (!this.touch || this.disabled || this.uploading) return;
			let originKey;
			let endKey;
			let tranX = e.touches[0].pageX - this.startX + this.oldX;
			let tranY = e.touches[0].pageY - this.startY + this.oldY;

			if (this.columns === 1) tranX = 0;
			this.tranX = tranX;
			this.tranY = tranY;
			originKey = item.key;
			endKey = this.calculateMoving(this.tranX, this.tranY);

			if (originKey == endKey || this.originKey == originKey) return;

			this.originKey = originKey;

			this.insert(originKey, endKey);
		},
		calculateMoving(tranX, tranY) {
			let rows = Math.ceil(this.list.length / this.columns) - 1,
				i = Math.round(tranX / this.item.width),
				j = Math.round(tranY / this.item.height);
			let endKey;

			i = i > this.columns - 1 ? this.columns - 1 : i;
			i = i < 0 ? 0 : i;

			j = j < 0 ? 0 : j;
			j = j > rows ? rows : j;

			endKey = i + this.columns * j;
			endKey = endKey >= this.list.length ? this.list.length - 1 : endKey;

			return endKey;
		},
		insert(origin, end) {
			let list;

			if (origin < end) {
				list = this.list.map(item => {
					if (item.key > origin && item.key <= end) {
						item.key = item.key - 1;
					} else if (item.key == origin) {
						item.key = end;
					}
					return item;
				});
				this.getPosition(list);
			} else if (origin > end) {
				list = this.list.map(item => {
					if (item.key >= end && item.key < origin) {
						item.key = item.key + 1;
					} else if (item.key == origin) {
						item.key = end;
					}
					return item;
				});
				this.getPosition(list);
			}
		},
		touchEnd(e) {
			if (!this.touch) return;

			this.clearData();
		},
		clearData() {
			this.touch = false;
			this.tranX = 0;
			this.tranY = 0;
			this.startX = 0;
			this.startY = 0;
			this.oldX = 0;
			this.oldY = 0;
			this.cur = -1;
			this.originKey = -1;

			setTimeout(() => {
				this.curZ = -1;
			}, 300);
		},
		removeItem(newKey, oldKey) {
			this.$emit('remove', {
				oldKey: oldKey,
				newKey: newKey
			});
			let list = this.list.map((item, index) => {
				if (item.key > newKey) item.key -= 1;
				return item;
			});
			list.splice(oldKey, 1);
			this.getPosition(list);
			setTimeout(() => {
				this.changeWrapHeight();
			}, 200);
		},
		preview(item) {
			let listData = [];
			this.list.forEach(item => {
				listData[item.key] = item.data.path;
			});
			this.$g.previewImage(item.data.path, listData);
		}
	}
};
</script>

<style lang="scss" scoped>
.item-wrap {
	position: relative;

	.item {
		position: absolute;
		width: 100%;
		z-index: 1;

		&.itemTransition {
			transition: transform 0.3s;
		}
		&.zIndex {
			z-index: 2;
		}
		&.cur {
			transition: initial;
		}
	}
}

.info {
	position: relative;
	padding-top: 100%;

	&-box {
		position: absolute;
		top: 0;
		left: 0;
		width: 100%;
		height: 100%;
		overflow: hidden;
		padding: 12rpx;
		box-sizing: border-box;

		&-image {
			width: 100%;
			height: 100%;
		}
	}
	.remove {
		position: absolute;
		top: 0;
		right: 0;
		color: #ff536b;
		z-index: 3;
	}
	.load-wrap {
		display: flex;
		align-items: center;
		justify-content: center;
		position: absolute;
		top: 12rpx;
		left: 12rpx;
		bottom: 12rpx;
		right: 12rpx;
		background-color: rgba($color: #000000, $alpha: 0.3);
		color: white;
		font-size: 32rpx;
		z-index: 2;
	}
}
</style>
